package org.cheniy.infrastructure.persistent.repository;

import lombok.extern.slf4j.Slf4j;
import org.cheniy.domain.strategy.model.entity.StrategyAwardEntity;
import org.cheniy.domain.strategy.model.entity.StrategyEntity;
import org.cheniy.domain.strategy.model.entity.StrategyRuleEntity;
import org.cheniy.domain.strategy.model.valobj.*;
import org.cheniy.domain.strategy.repository.IStrategyRepository;
import org.cheniy.domain.strategy.service.rule.chain.factory.DefaultChainFactory;
import org.cheniy.infrastructure.persistent.dao.*;
import org.cheniy.infrastructure.persistent.po.*;
import org.cheniy.infrastructure.redis.IRedisService;
import org.cheniy.types.common.Constants;
import org.cheniy.types.exception.AppException;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RMap;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static org.cheniy.types.enums.ResponseCode.UN_ASSEMBLED_STRATEGY_ARMORY;

/**
 * @author cheniy
 * @description Strategy中Repository的实现
 * @create 2025/3/1 23:48
 */
@Slf4j
@Repository
public class StrategyRepository implements IStrategyRepository {

    @Resource
    private IStrategyAwardDao iStrategyAwardDao;
    @Resource
    private IStrategyDao iStrategyDao;
    @Resource
    private IStrategyRuleDao iStrategyRuleDao;
    @Resource
    private IRedisService iRedisService;
    @Resource
    private IRuleTreeDao iRuleTreeDao;
    @Resource
    private IRuleTreeNodeDao iRuleTreeNodeDao;
    @Resource
    private IRuleTreeNodeLineDao iRuleTreeNodeLineDao;

    @Resource
    private IRaffleActivityDao iRaffleActivityDao;

    @Resource
    private IRaffleActivityAccountDayDao iRaffleActivityAccountDayDao;
    @Resource
    private IRaffleActivityAccountDao raffleActivityAccountDao;






    @Override
    public List<StrategyAwardEntity> queryStrategyAwardList(Long strategyId) {

        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_LIST_KEY + strategyId;


        // 先从redis中去查询时候有数据
        List<StrategyAwardEntity> strategyAwardEntities = iRedisService.getValue(cacheKey);
        // 没有就从数据库里面去查
        if (null != strategyAwardEntities && !strategyAwardEntities.isEmpty()) return strategyAwardEntities;
        List<StrategyAward> strategyAwards = iStrategyAwardDao.queryStrategyAwardListByStrategyId(strategyId);
            // 创建返回列表
            strategyAwardEntities = new ArrayList<>(strategyAwards.size());
            for (StrategyAward strategyAward : strategyAwards) {
                StrategyAwardEntity strategyAwardEntity = StrategyAwardEntity.builder()
                        .awardId(strategyAward.getAwardId())
                        .awardCount(strategyAward.getAwardCount())
                        .awardCountSurplus(strategyAward.getAwardCountSurplus())
                        .awardSubtitle(strategyAward.getAwardSubtitle())
                        .awardTitle(strategyAward.getAwardTitle())
                        .awardRate(strategyAward.getAwardRate())
                        .ruleModels(strategyAward.getRuleModels())
                        .strategyId(strategyAward.getStrategyId())
                        .sort(strategyAward.getSort()).build();
                strategyAwardEntities.add(strategyAwardEntity);
            }
            iRedisService.setValue(cacheKey,strategyAwardEntities);
            return strategyAwardEntities;
    }

    @Override
    public void storeStrategyAwardSearchRateTable(String strategyId, BigDecimal rateRange, HashMap<String, String> shuffleStrategyAwardSearchRateTables) {

        // 1. 存储抽奖策略的范围值，如10000，用于生成10000以内的随机数
        iRedisService.setValue(Constants.RedisKey.STRATEGY_AWARD_RANGE_KEY+strategyId,rateRange.intValue());

        // 2. 存储概率查找表
        RMap<String, String> cacheRateTable = iRedisService.getMap(Constants.RedisKey.STRATEGY_AWARD_TABLE_KEY + strategyId);
        cacheRateTable.putAll(shuffleStrategyAwardSearchRateTables);

    }

    @Override
    public Integer getRateRange(Long strategyId) {
        return  getRateRange(String.valueOf(strategyId));
    }

    @Override
    public Integer getRateRange(String key) {
        String cacheStrategyAwardRangeKey = Constants.RedisKey.STRATEGY_AWARD_RANGE_KEY + key;
        if (!iRedisService.isExists(cacheStrategyAwardRangeKey)){
            throw new AppException(UN_ASSEMBLED_STRATEGY_ARMORY.getCode(),cacheStrategyAwardRangeKey+Constants.COLON+UN_ASSEMBLED_STRATEGY_ARMORY.getInfo());
        }
        return  iRedisService.getValue(cacheStrategyAwardRangeKey);
    }

    @Override
    public Integer getAwardMapAwardIdByRandom(String strategyId, int rateRandom) {
        // 获取redis中的map表，输入随机数作为字段，获取到奖品id
        String cacheStrategyAwardTableKey = Constants.RedisKey.STRATEGY_AWARD_TABLE_KEY + strategyId;
        return Integer.valueOf(iRedisService.getFromMap(cacheStrategyAwardTableKey, String.valueOf(rateRandom)));
    }

    @Override
    public StrategyEntity queryStrategyByStrategyId(Long strategyId) {
        String cacheKey = Constants.RedisKey.STRATEGY_KEY + strategyId;

        // 先从redis中去查询时候有数据
        StrategyEntity strategyEntity = iRedisService.getValue(cacheKey);
        // 没有就从数据库里面去查
        if (null != strategyEntity ) return strategyEntity;
        Strategy strategy= iStrategyDao.queryStrategyByStrategyId(strategyId);

        strategyEntity=StrategyEntity.builder()
                .strategyId(strategy.getStrategyId())
                .strategyDesc(strategy.getStrategyDesc())
                .ruleModels(strategy.getRuleModels())
                .build();
        return strategyEntity;
    }

    @Override
    public StrategyRuleEntity queryStrategyRule(Long strategyId, String ruleWeight) {

        StrategyRule strategyRule= iStrategyRuleDao.queryStrategyRuleByStrategyIdRuleWeight(strategyId,ruleWeight);

        StrategyRuleEntity strategyRuleEntity=StrategyRuleEntity.builder()
                .awardId(strategyRule.getAwardId())
                .ruleDesc(strategyRule.getRuleDesc())
                .ruleModel(strategyRule.getRuleModel())
                .ruleType(strategyRule.getRuleType())
                .ruleValue(strategyRule.getRuleValue())
                .strategyId(strategyRule.getStrategyId())
                .build();
        return strategyRuleEntity;
    }

    @Override
    public String queryStrategyRuleValue(Long strategyId, Integer awardId, String ruleModel) {

        StrategyRule strategyRule = new StrategyRule();
        strategyRule.setStrategyId(strategyId);
        strategyRule.setAwardId(awardId);
        strategyRule.setRuleModel(ruleModel);
        return iStrategyRuleDao.queryStrategyRuleValue(strategyRule);
    }

    @Override
    public StrategyAwardRuleModelVO queryStrategyAwardRule(Long strategyId, Integer awardId) {

        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setAwardId(awardId);
        strategyAward.setStrategyId(strategyId);
        String ruleModels= iStrategyAwardDao.queryStrategyAwardRule(strategyAward);
        return StrategyAwardRuleModelVO.builder().ruleModels(ruleModels).build();
    }

    @Override
    public RuleTreeVO queryRuleTreeVOByTreeId(String treeId) {

        // 先从redis中去查询时候有数据
        String cacheKey = Constants.RedisKey.RULE_TREE_VO_KEY + treeId;
        RuleTreeVO ruleTreeVOCache = iRedisService.getValue(cacheKey);
        if (null != ruleTreeVOCache ) return ruleTreeVOCache;
        // 没有就从数据库里面去查


        // 1. 先查询相关的规则树，节点列表，节点连线规则列表
        RuleTree ruleTree=iRuleTreeDao.queryRuleTreeByTreeId(treeId);
        List<RuleTreeNode> ruleTreeNodeList=iRuleTreeNodeDao.queryRuleTreeNodeListByTreeId(treeId);
        List<RuleTreeNodeLine> ruleTreeNodeLineList=iRuleTreeNodeLineDao.queryRuleTreeNodeLineListByTreeId(treeId);


        // 2. 节点连线list改造为map（节点的名称作为key，连线list作为value），搭建节点连线
        Map<String, List<RuleTreeNodeLineVO>> ruleTreeNodeLineVOMap = new HashMap<>();
        for (RuleTreeNodeLine ruleTreeNodeLine:ruleTreeNodeLineList){
            RuleTreeNodeLineVO ruleTreeNodeLineVO = RuleTreeNodeLineVO.builder()
                    .treeId(ruleTreeNodeLine.getTreeId())
                    .ruleNodeTo(ruleTreeNodeLine.getRuleNodeTo())
                    .ruleNodeFrom(ruleTreeNodeLine.getRuleNodeFrom())
                    .ruleLimitType(RuleLimitTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitType()))
                    .ruleLimitValue(RuleLogicCheckTypeVO.valueOf(ruleTreeNodeLine.getRuleLimitValue()))
                    .build();

            // ruleTreeNodeLine.getRuleNodeFrom()作为map的key值，
            // 如果存在key对应的value存在则返回value，不存在则返回new ArrayList<>()并将key和返回的list存入map
            List<RuleTreeNodeLineVO> ruleTreeNodeLineVOS = ruleTreeNodeLineVOMap.computeIfAbsent(ruleTreeNodeLine.getRuleNodeFrom(), k -> new ArrayList<RuleTreeNodeLineVO>());
            ruleTreeNodeLineVOS.add(ruleTreeNodeLineVO);
        }

        // 3. 节点list改造为map，搭建节点
        Map<String, RuleTreeNodeVO> ruleTreeNodeVOMap = new HashMap<>();
        for (RuleTreeNode ruleTreeNode:ruleTreeNodeList){
            RuleTreeNodeVO ruleTreeNodeVO = RuleTreeNodeVO.builder()
                    .treeId(ruleTreeNode.getTreeId())
                    .ruleKey(ruleTreeNode.getRuleKey())
                    .ruleDesc(ruleTreeNode.getRuleDesc())
                    .ruleValue(ruleTreeNode.getRuleValue())
                    .treeNodeLineVOList(ruleTreeNodeLineVOMap.get(ruleTreeNode.getRuleKey()))
                    .build();

            ruleTreeNodeVOMap.put(ruleTreeNodeVO.getRuleKey(),ruleTreeNodeVO);
        }

        // 4. 搭建规则树
        RuleTreeVO ruleTreeVO = RuleTreeVO.builder()
                .treeId(ruleTree.getTreeId())
                .treeName(ruleTree.getTreeName())
                .treeDesc(ruleTree.getTreeDesc())
                .treeRootRuleNode(ruleTree.getTreeNodeRuleKey())
                .treeNodeMap(ruleTreeNodeVOMap)
                .build();

        // 5. 存入到redis中
        iRedisService.setValue(cacheKey,ruleTreeVO);


        return ruleTreeVO;
    }



    @Override
    public void cacheStrategyAwardCount(String cacheKey, Integer awardCount) {

        // if (0!=iRedisService.getAtomicLong(cacheKey)) return;
        iRedisService.setAtomicLong(cacheKey,awardCount);
    }

    @Override
    public Boolean subtractionAwardStock(String cacheKey) {
        return subtractionAwardStock(cacheKey, null);
    }

    @Override
    public Boolean subtractionAwardStock(String cacheKey, Date endDateTime) {
        long surplus = iRedisService.decr(cacheKey);
        if (surplus < 0) {
            // 库存小于0，恢复为0个
            iRedisService.setAtomicLong(cacheKey, 0);
            return false;
        }
        // 1. 按照cacheKey decr 后的值，如 99、98、97 和 key 组成为库存锁的key进行使用。
        // 2. 加锁为了兜底，如果后续有恢复库存，手动处理等，也不会超卖。因为所有的可用库存key，都被加锁了。
        String lockKey = cacheKey + Constants.UNDERLINE + surplus;
        Boolean lock = false;
        if (null != endDateTime) {
            long expireMillis = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
            lock = iRedisService.setNx(lockKey, expireMillis, TimeUnit.MILLISECONDS);
        } else {
            lock = iRedisService.setNx(lockKey);
        }
        if (!lock) {
            log.info("策略奖品库存加锁失败 {}", lockKey);
        }
        return lock;
    }


    @Override
    public void awardStockConsumeSendQueue(StrategyAwardStockKeyVO strategyAwardStockKeyVO) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUEUE_KEY;
        // 获取到阻塞队列
        RBlockingQueue<StrategyAwardStockKeyVO> blockingQueue = iRedisService.getBlockingQueue(cacheKey);
        // 在阻塞队列基础上获取到延迟队列
        RDelayedQueue<StrategyAwardStockKeyVO> delayedQueue = iRedisService.getDelayedQueue(blockingQueue);
        // 设置3s后该strategyAwardStockKeyVO才能被取出
        delayedQueue.offer(strategyAwardStockKeyVO,3, TimeUnit.SECONDS);

    }

    @Override
    public StrategyAwardStockKeyVO takeQueueValue() {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_QUEUE_KEY;
        RBlockingQueue<StrategyAwardStockKeyVO> destinationQueue = iRedisService.getBlockingQueue(cacheKey);

        return destinationQueue.poll();
    }

    @Override
    public void updateStrategyAwardStock(Long strategyId, Integer awardId) {
        StrategyAward strategyAward = new StrategyAward();
        strategyAward.setStrategyId(strategyId);
        strategyAward.setAwardId(awardId);
        iStrategyAwardDao.updateStrategyAwardStock(strategyAward);
    }

    @Override
    public StrategyAwardEntity queryStrategyAwardEntity(Long strategyId, Integer awardId) {

        // 先从redis中去查询时候有数据
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_KEY +strategyId+Constants.UNDERLINE+ awardId;
        StrategyAwardEntity strategyAwardEntity = iRedisService.getValue(cacheKey);
        if (null != strategyAwardEntity ) return strategyAwardEntity;
        // 数据库查询数据
        StrategyAward strategyReq = new StrategyAward();
        strategyReq.setAwardId(awardId);
        strategyReq.setStrategyId(strategyId);
        StrategyAward strategyAward=iStrategyAwardDao.queryStrategyAward(strategyReq);
        // 转换数据
        strategyAwardEntity = StrategyAwardEntity.builder()
                .strategyId(strategyAward.getStrategyId())
                .awardSubtitle(strategyAward.getAwardSubtitle())
                .awardTitle(strategyAward.getAwardTitle())
                .awardRate(strategyAward.getAwardRate())
                .awardId(strategyAward.getAwardId())
                .awardCount(strategyAward.getAwardCount())
                .awardCountSurplus(strategyAward.getAwardCountSurplus())
                .sort(strategyAward.getSort())
                .ruleModels(strategyReq.getRuleModels())
                .build();
        // 写入缓存
        iRedisService.setValue(cacheKey,strategyAwardEntity);
        return strategyAwardEntity;
    }

    @Override
    public Long queryStrategyIdByActivityId(Long activityId) {
        return iRaffleActivityDao.queryStrategyIdByActivityId(activityId);
    }

    @Override
    public Integer queryTodayUserRaffleCount(String userId, Long strategyId) {
        // 活动ID
        Long activityId = iRaffleActivityDao.queryActivityIdByStrategyId(strategyId);
        // 封装参数
        RaffleActivityAccountDay raffleActivityAccountDayReq = new RaffleActivityAccountDay();
        raffleActivityAccountDayReq.setUserId(userId);
        raffleActivityAccountDayReq.setActivityId(activityId);
        raffleActivityAccountDayReq.setDay(raffleActivityAccountDayReq.currentDay());
        RaffleActivityAccountDay raffleActivityAccountDay = iRaffleActivityAccountDayDao.queryActivityAccountDayByUserId(raffleActivityAccountDayReq);
        if (null == raffleActivityAccountDay) return 0;
        // 总次数 - 剩余的，等于今日参与的
        return raffleActivityAccountDay.getDayCount() - raffleActivityAccountDay.getDayCountSurplus();
    }

    @Override
    public Map<String, Integer> queryAwardRuleLockCount(String[] treeIds) {
        if (null == treeIds || treeIds.length == 0) return new HashMap<>();
        List<RuleTreeNode> ruleTreeNodes = iRuleTreeNodeDao.queryRuleLocks(treeIds);
        Map<String, Integer> resultMap = new HashMap<>();
        for (RuleTreeNode node : ruleTreeNodes) {
            String treeId = node.getTreeId();
            Integer ruleValue = Integer.valueOf(node.getRuleValue());
            resultMap.put(treeId, ruleValue);
        }
        return resultMap;
    }

    @Override
    public Integer queryActivityAccountTotalUseCount(String userId, Long strategyId) {
        Long activityId = iRaffleActivityDao.queryActivityIdByStrategyId(strategyId);
        RaffleActivityAccount raffleActivityAccount = raffleActivityAccountDao.queryActivityAccountByUserId(RaffleActivityAccount.builder()
                .userId(userId)
                .activityId(activityId)
                .build());
        // 返回计算使用量
        return raffleActivityAccount.getTotalCount() - raffleActivityAccount.getTotalCountSurplus();
    }

    @Override
    public List<RuleWeightVO> queryAwardRuleWeight(Long strategyId) {
        // 优先从缓存获取
        String cacheKey = Constants.RedisKey.STRATEGY_RULE_WEIGHT_KEY + strategyId;
        List<RuleWeightVO> ruleWeightVOS = iRedisService.getValue(cacheKey);
        if (null != ruleWeightVOS) return ruleWeightVOS;

        ruleWeightVOS = new ArrayList<>();
        // 1. 查询权重规则配置
        StrategyRule strategyRuleReq = new StrategyRule();
        strategyRuleReq.setStrategyId(strategyId);
        strategyRuleReq.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
        String ruleValue = iStrategyRuleDao.queryStrategyRuleValue(strategyRuleReq);
        // 2. 借助实体对象转换规则
        StrategyRuleEntity strategyRuleEntity = new StrategyRuleEntity();
        strategyRuleEntity.setRuleModel(DefaultChainFactory.LogicModel.RULE_WEIGHT.getCode());
        strategyRuleEntity.setRuleValue(ruleValue);
        // 这里出现实体生成的是Map<String, List<String>> ，我需要Map<String, List<Integer>>
        // TODO 下面暂时将List<String>转化为List<Integer>，待优化

        Map<String, List<String>> ruleWeightValues = strategyRuleEntity.getRuleWeightValues();
        // 3. 遍历规则组装奖品配置
        Set<String> ruleWeightKeys = ruleWeightValues.keySet();
        for (String ruleWeightKey : ruleWeightKeys) {
            List<String> awardIds = ruleWeightValues.get(ruleWeightKey);
            List<RuleWeightVO.Award> awardList = new ArrayList<>();
            ArrayList<Integer> tempArrayList = new ArrayList<>();
            // 也可以修改为一次从数据库查询
            for (String awardId : awardIds) {
                StrategyAward strategyAwardReq = new StrategyAward();
                strategyAwardReq.setStrategyId(strategyId);
                strategyAwardReq.setAwardId(Integer.valueOf(awardId));
                StrategyAward strategyAward = iStrategyAwardDao.queryStrategyAward(strategyAwardReq);
                awardList.add(RuleWeightVO.Award.builder()
                        .awardId(strategyAward.getAwardId())
                        .awardTitle(strategyAward.getAwardTitle())
                        .build());
                tempArrayList.add(Integer.valueOf(awardId));
            }

            ruleWeightVOS.add(RuleWeightVO.builder()
                    .ruleValue(ruleValue)
                    .weight(Integer.valueOf(ruleWeightKey.split(Constants.COLON)[0]))
                    // .awardIds(awardIds)
                    .awardIds(tempArrayList)
                    .awardList(awardList)
                    .build());
        }

        // 设置缓存 - 实际场景中，这类数据，可以在活动下架的时候统一清空缓存。
        iRedisService.setValue(cacheKey, ruleWeightVOS);

        return ruleWeightVOS;
    }

}
